package com.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.net.ssl.SSLContext;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;


@SuppressWarnings("deprecation")
class AnyTrustStrategy implements TrustStrategy{

	public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		return true;
	}

}

public class HttpUtilFinal {

	private static final Log log= LogFactory.getLog(HttpUtilFinal.class);

	private static final int REQUEST_TIMEOUT = 15 * 1000; // 设置请求超时

    public static final int TIMEOUT         = 15 * 1000; // 连接超时时间

	public static final int SO_TIMEOUT      = 60 * 1000; // 数据传输超时

	private static int bufferSize= 1024;

	private static volatile HttpUtilFinal instance;

	private ConnectionConfig connConfig;

	private SocketConfig socketConfig;

	private ConnectionSocketFactory plainSF;

	private KeyStore trustStore;

	private SSLContext sslContext;

	private LayeredConnectionSocketFactory sslSF;

	private Registry<ConnectionSocketFactory> registry;

	private PoolingHttpClientConnectionManager connManager;

	private volatile HttpClient client;

	private volatile BasicCookieStore cookieStore;

	public static String defaultEncoding= "utf-8";

	@SuppressWarnings("deprecation")
	private static List<NameValuePair> paramsConverter(Map<String, String> params){
		List<NameValuePair> nvps = new LinkedList<>();
		Set<Entry<String, String>> paramsSet= params.entrySet();
		for (Entry<String, String> paramEntry : paramsSet) {
			if(paramEntry.getKey().equals("sku") || paramEntry.getKey().equals("parent_sku")){
				nvps.add(new BasicNameValuePair(paramEntry.getKey(), StringEscapeUtils.unescapeHtml4(paramEntry.getValue())));
			}else{
				nvps.add(new BasicNameValuePair(paramEntry.getKey(), paramEntry.getValue()));
			}
		}
		return nvps;
	}

    public static String readStream(InputStream in, String encoding) {
        if (in == null) {
            return null;
        }
        InputStreamReader inReader = null;
        try {
            inReader = new InputStreamReader(in, Strings.isNullOrEmpty(encoding) ? defaultEncoding : encoding);
            char[] buffer = new char[bufferSize];
            int readLen;
            StringBuilder sb = new StringBuilder();
            while (0 < (readLen = inReader.read(buffer))) {
                sb.append(buffer, 0, readLen);
            }
            return sb.toString();
        } catch (IOException e) {
            log.error("读取返回内容出错", e);
        } finally {
            if (inReader != null) {
                try {
                    inReader.close();
                } catch (IOException ignore) {
                }
            }
        }
        return null;
    }

	public void doPdf(HttpGet httpGet, String url,OutputStream os) {
		try {
			RequestConfig requestConfig = RequestConfig.custom()
					.setConnectTimeout(TIMEOUT)
					.setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
			httpGet.setConfig(requestConfig);
			URIBuilder builder = new URIBuilder(url);
			httpGet.setURI(builder.build());
			HttpResponse response = client.execute(httpGet);
			HttpUtilFinal.readPdf(response != null ? response.getEntity().getContent() : null, os, null);
		} catch (Exception e) {
			log.error(e);
		}
	}

	public void doPostPdf(HttpPost httpPost, String url,OutputStream os) {
		try {
			RequestConfig requestConfig = RequestConfig.custom()
					.setConnectTimeout(TIMEOUT)
					.setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
			httpPost.setConfig(requestConfig);
			URIBuilder builder = new URIBuilder(url);
			httpPost.setURI(builder.build());
			HttpResponse response = client.execute(httpPost);
			HttpUtilFinal.readPdf(response != null ? response.getEntity().getContent() : null, os, null);
		} catch (Exception e) {
			log.error(e);
		}
	}

	public static void readPdf(InputStream in,OutputStream os, String encoding) {
		if (in == null) {
			return ;
		}
		try {
			byte[] buf = new byte[255];
			int len;
			while ((len = in.read(buf)) != -1) {
				os.write(buf, 0, len);
			}
			os.flush();
			os.close();
		} catch (IOException e) {
			log.error("读取返回内容出错", e);
		}
	}

	@SuppressWarnings({ "deprecation" })
	private HttpUtilFinal(){
		//设置连接参数
		connConfig = ConnectionConfig.custom().setCharset(Charset.forName(defaultEncoding)).build();
		socketConfig = SocketConfig.custom().setSoTimeout(SO_TIMEOUT).build();
		RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create();
		plainSF = new PlainConnectionSocketFactory();
		registryBuilder.register("http", plainSF);
		//指定信任密钥存储对象和连接套接字工厂
		try {
			trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore, new AnyTrustStrategy()).build();
			sslSF = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
			registryBuilder.register("https", sslSF);
		} catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
        registry = registryBuilder.build();
		//设置连接管理器
		connManager = new PoolingHttpClientConnectionManager(registry);
		connManager.setDefaultConnectionConfig(connConfig);
		connManager.setDefaultSocketConfig(socketConfig);
		connManager.setMaxTotal(200);
		connManager.setDefaultMaxPerRoute(100);
		//指定cookie存储对象
		cookieStore = new BasicCookieStore();
		//构建客户端
		client= HttpClientBuilder.create().setDefaultCookieStore(cookieStore).setConnectionManager(connManager).build();
	}

	public static HttpUtilFinal getInstance(){
		synchronized (HttpUtilFinal.class) {
			if (HttpUtilFinal.instance == null){
				instance = new HttpUtilFinal();
			}
			return instance;
		}
	}

	public InputStream doGet(String url) throws URISyntaxException, IOException{
		HttpResponse response= this.doGet(url, null, null,null);
		return response!=null ? response.getEntity().getContent() : null;
	}

	public String doGetForString(String url,String encoding) throws URISyntaxException, IOException{
		return HttpUtilFinal.readStream(this.doGet(url), encoding);
	}

	public InputStream doGetForStream(String url, Map<String, String> queryParams, HttpHost proxy, Map<String,String> headerMap) throws URISyntaxException, IOException{
		HttpResponse response= this.doGet(url, queryParams, proxy, headerMap);
		return response!=null ? response.getEntity().getContent() : null;
	}

	public String doGetForString(String url, Map<String, String> queryParams, HttpHost proxy, Map<String,String> headerMap) throws URISyntaxException, IOException{
		return HttpUtilFinal.readStream(this.doGetForStream(url, queryParams, proxy, headerMap), null);
	}

	/**
	 * 基本的Get请求
	 * @param url 请求url
	 * @param queryParams 请求头的查询参数
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public HttpResponse doGet(String url, Map<String, String> queryParams, HttpHost proxy, Map<String,String> headerMap) throws URISyntaxException, IOException{
		HttpGet gm = new HttpGet();

		RequestConfig requestConfig;
		if(proxy != null){
			requestConfig = RequestConfig.custom()
	                .setConnectTimeout(TIMEOUT)
	                .setConnectionRequestTimeout(REQUEST_TIMEOUT).setProxy(proxy).build();
		}else{
			requestConfig = RequestConfig.custom()
	                .setConnectTimeout(TIMEOUT)
	                .setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
		}
		gm.setConfig(requestConfig);

		URIBuilder builder = new URIBuilder(url);
		//填入查询参数
		if (queryParams!=null && !queryParams.isEmpty()){
			builder.setParameters(HttpUtilFinal.paramsConverter(queryParams));
		}
		if(headerMap != null && !headerMap.isEmpty()){
			for (Map.Entry<String, String> entry : headerMap.entrySet()) {
				gm.setHeader(entry.getKey(), entry.getValue());
			}
		}
		gm.setURI(builder.build());
		return client.execute(gm);
	}

	public InputStream doPostForStream(String url, Map<String, String> queryParams) throws URISyntaxException, IOException {
		HttpResponse response = this.doPost(url, queryParams, null, null, null);
		return response!=null ? response.getEntity().getContent() : null;
	}

	public String doPostForString(String url, Map<String, String> queryParams) throws URISyntaxException, IOException {
		return HttpUtilFinal.readStream(this.doPostForStream(url, queryParams), null);
	}

	public InputStream doPostForStream(String url, Map<String, String> queryParams, Map<String, String> formParams, Map<String,String> headerMap, HttpHost proxy) throws URISyntaxException, IOException{
		HttpResponse response = this.doPost(url, queryParams, formParams, headerMap, proxy);
		return response!=null ? response.getEntity().getContent() : null;
	}

	public String doPostRetString(String url, Map<String, String> queryParams, Map<String, String> formParams, Map<String,String> headerMap, HttpHost proxy, String readEncode) throws URISyntaxException, IOException{
		return HttpUtilFinal.readStream(this.doPostForStream(url, queryParams, formParams, headerMap, proxy), readEncode);
	}

	public void postForStream(String url, String xmlData, OutputStream os,Map<String, String> keys){
		HttpPost pm = new HttpPost();
		try {
			RequestConfig requestConfig = RequestConfig.custom()
	                .setConnectTimeout(TIMEOUT)
	                .setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
			pm.setConfig(requestConfig);

			for (Map.Entry<String, String> entry : keys.entrySet()) {
				pm.setHeader(entry.getKey(), entry.getValue());
            }
			pm.setHeader("Content-Type", "text/xml;charset=utf-8");
			StringEntity strEntity = new StringEntity(xmlData, "UTF-8");

			pm.setEntity(strEntity);
			URIBuilder builder = new URIBuilder(url);
			pm.setURI(builder.build());
			HttpResponse httpResponse = client.execute(pm);

			HttpEntity httpEntity = httpResponse.getEntity();
	        httpEntity.writeTo(os);
		} catch (Exception e) {
			log.error("postForStream:",e);
		}
	}

	/**
	 * 执行一个HTTP POST请求,参数为XML类型的字符串
	 * @param url		地址
	 * @return String
	 */
	public String doPostXml(HttpPost httpPost, String url) {
		try {
			RequestConfig requestConfig = RequestConfig.custom()
	                .setConnectTimeout(TIMEOUT)
	                .setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
			httpPost.setConfig(requestConfig);
			URIBuilder builder = new URIBuilder(url);
			httpPost.setURI(builder.build());
			HttpResponse response = client.execute(httpPost);
			log.info(response!=null ? response.getStatusLine().getStatusCode() : "没有代码");
			return HttpUtilFinal.readStream(response!=null ? response.getEntity().getContent() : null, null);
		} catch (Exception e) {
			log.error(e);
		}
		return "";
	}
	/**
	 * 打印延时2min
	 */
	public String doPostXmlPDF(HttpPost httpPost, String url) {
		try {
			RequestConfig requestConfig = RequestConfig.custom()
					.setConnectTimeout(TIMEOUT).setSocketTimeout(120*1000)
					.setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
			httpPost.setConfig(requestConfig);
			URIBuilder builder = new URIBuilder(url);
			httpPost.setURI(builder.build());
			HttpResponse response = client.execute(httpPost);
			log.info(response!=null ? response.getStatusLine().getStatusCode() : "没有代码");
			return HttpUtilFinal.readStream(response!=null ? response.getEntity().getContent() : null, null);
		} catch (Exception e) {
			log.error(e);
		}
		return "";
	}

	/**
	 * 执行一个HTTP POST请求,参数为XML类型的字符串
	 * @param url		地址
	 * @return String
	 */
	public Map<String,Object> doPostJson(HttpPost httpPost, String url) {
		Map<String,Object> maps = Maps.newHashMap();
		try {
			RequestConfig requestConfig = RequestConfig.custom()
	                .setConnectTimeout(TIMEOUT)
	                .setConnectionRequestTimeout(REQUEST_TIMEOUT)
					.setSocketTimeout(SO_TIMEOUT).build();
			httpPost.setConfig(requestConfig);
			URIBuilder builder = new URIBuilder(url);
			httpPost.setURI(builder.build());
			HttpResponse response = client.execute(httpPost);
			maps.put("ret",HttpUtilFinal.readStream(response!=null ? response.getEntity().getContent() : null, null));
			maps.put("status",response == null ? -1 : response.getStatusLine().getStatusCode());
		} catch (Exception e) {
			log.error(e);
		}
		return maps;
	}

	/**
	 * 执行一个HTTP GET请求,参数为XML类型的字符串
	 * @param url		地址
	 * @return String
	 */
	public String doGetXml(HttpGet httpGet, String url) {
		try {
			RequestConfig requestConfig = RequestConfig.custom()
	                .setConnectTimeout(TIMEOUT)
	                .setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
			httpGet.setConfig(requestConfig);
			URIBuilder builder = new URIBuilder(url);
			httpGet.setURI(builder.build());
			HttpResponse response = client.execute(httpGet);
			return HttpUtilFinal.readStream(response!=null ? response.getEntity().getContent() : null, null);
		} catch (Exception e) {
			log.error(e);
		}
		return "";
	}

	/**
	 * 基本的Post请求
	 * @param url 请求url
	 * @param queryParams 请求头的查询参数
	 * @param formParams post表单的参数
	 * @throws URISyntaxException
	 * @throws IOException
	 */
    public HttpResponse doPost(String url, Map<String, String> queryParams, Map<String, String> formParams, Map<String,String> headerMap, HttpHost proxy) throws URISyntaxException, IOException {
        HttpPost pm = null;
        try {
            pm = new HttpPost();
            RequestConfig requestConfig;

            if (proxy != null) {
                requestConfig = RequestConfig.custom()
                        .setConnectTimeout(TIMEOUT)
                        .setConnectionRequestTimeout(REQUEST_TIMEOUT).setProxy(proxy).build();
            } else {
                requestConfig = RequestConfig.custom()
                        .setConnectTimeout(TIMEOUT)
                        .setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
            }

            pm.setConfig(requestConfig);

            if(headerMap != null && !headerMap.isEmpty()){
                for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                    pm.setHeader(entry.getKey(), entry.getValue());
                }
            }

            URIBuilder builder = new URIBuilder(url);
            //填入查询参数
            if (queryParams != null && !queryParams.isEmpty()) {
                builder.setParameters(HttpUtilFinal.paramsConverter(queryParams));
            }
            pm.setURI(builder.build());
            //填入表单参数
            if (formParams != null && !formParams.isEmpty()) {
                pm.setEntity(new UrlEncodedFormEntity(HttpUtilFinal.paramsConverter(formParams), defaultEncoding));
            }
        } finally {
            if (pm != null) {
                pm.releaseConnection();
            }
        }
        return client.execute(pm);
    }
}
